using System;
using System.Collections;
using System.Data;
using System.Text;
using System.Windows.Forms;
using ctsu.Admin.Forms;
using ctsu.BaseForms;

using Syncfusion.Windows.Forms.Tools;
using ctsu.Data;

namespace ctsu.Admin.BusLogic
{
	public class FieldContextManager : IFieldContextManager
	{
		public event BusinessLogicEvents.BusinessEventNotifyHandler OnAfterHookEvents;
		public event BusinessLogicEvents.BusinessEventNotifyHandler OnAfterLoadMetaDataEvents;
		public event BusinessLogicEvents.BusinessEventNotifyHandler OnAfterCreateFieldContexts;
		public event BusinessLogicEvents.BusinessEventNotifyHandler OnAfterBindFieldContextsToControls;
		private DataRow _currentRow; //actual data table row with current data
		private DataTable _dbTable;	//current data table	
		private ArrayList _alItems; // ordered list of fieldContext objects
		private IBLFormBase _businessLogic;
		private Hashtable _fieldHashtable; //lookup list of fieldContext objects
		private Hashtable _tableKeys;
		private ArrayList _tableKeysOrdered;

		public FieldContextManager(IBLFormBase businessLogic)
		{
			this._businessLogic = businessLogic;
		}

		public IBLFormBase BusinessLogic
		{
			get { return this._businessLogic; }
			set { this._businessLogic = value; }
		}

		public ArrayList Items
		{
			get { return this._alItems; }
			set { this._alItems = value; }
		}

		public Hashtable TableKeys
		{
			get { return _tableKeys; }
		}

		public ArrayList TableKeysOrdered
		{
			get { return _tableKeysOrdered; }
		}

		public Hashtable FieldHashtable
		{
			get { return this._fieldHashtable; }
			set { this._fieldHashtable = value; }
		}

		public DataTable DbTable
		{
			get { return this._dbTable; }
			set { this._dbTable = value; }
		}

		public DataRow CurrentRow
		{
			get { return this._currentRow; }
			set { this._currentRow = value; }
		}

		public bool IsRowDirty
		{
			get
			{
				if (this._currentRow == null)
					return false;
				if (this._currentRow.RowState == DataRowState.Unchanged)
					return false;
				return true;
			}
		}

		public IFieldContext Item(string key)
		{
			if (this._fieldHashtable.Contains(key))
				return (FieldContext)this._fieldHashtable[key];
			return null;
		}

		public bool Contains(string key)
		{
			if (this._fieldHashtable.Contains(key))
				return true;
			return false;
		}

		public string GetUniqueFieldNotKey()
		{
			//Getting the unique key from FillSchema is
			//not currently supported
			foreach (FieldContext vc in _alItems)
			{
				if (vc.IsUnique && (!vc.IsKey))
					return vc.Column.Caption;
			}
			return "";
		}

		#region one time methods for initializing field contexts
		public void CreateFieldContexts(DataTable dt, String[] asExclude, string uniqueField)
		{
			_alItems = new ArrayList();
			_fieldHashtable = new Hashtable();
			FieldContext vc;
			bool isExclude = false;
			_tableKeys = new Hashtable();
			_tableKeysOrdered = new ArrayList();

			foreach (DataColumn dc in dt.Columns)
			{
				if (asExclude != null)
				{
					for (int i = 0; i < asExclude.Length; i++)
					{
						if (dc.ColumnName.Equals(asExclude[i]))
						{
							isExclude = true;
							break;
						}
					}
				}

				if (isExclude)
				{
					isExclude = false;
					continue;
				}

				vc = new FieldContext();

				if (DataUtils.IsKey(dc.ColumnName, dt))
				{
					_tableKeys.Add(dc.ColumnName, dc);
					_tableKeysOrdered.Add(dc.ColumnName);
					vc.IsKey = true;
				}

				if (dc.ColumnName == uniqueField)
					vc.IsUnique = true;

				_fieldHashtable.Add(dc.ColumnName, vc);
				_alItems.Add(vc);
				vc.Manager = this;
				vc.Column = dc;
			}
			if (OnAfterCreateFieldContexts != null)
				OnAfterCreateFieldContexts();
		}

		public bool BindFieldContextsToControls()
		{
			foreach (FieldContext vc in _alItems)
			{
				vc.Control = Utils.GetControlByName(vc.Column.ColumnName, this._businessLogic.Form.Controls);
				if (vc.Control != null)
					vc.Control.Tag = vc;
			}
			if (OnAfterBindFieldContextsToControls != null)
				OnAfterBindFieldContextsToControls();
			return true;
		}

		public bool LoadMetadata()
		{
			StringBuilder sb = new StringBuilder();
			string tableName = this._businessLogic.FormInfo.TableName + "Meta";
			//string lableTableName = this._businessLogic.FormInfo.TableName + "Labels";
			string sql = "SELECT * FROM " + tableName;
			//string sql2 = "SELECT * FROM " + lableTableName;

			IDataReader reader = null;

			int ord = 0;
			string sVal = "";

			//SqlConnection conn = LoginForm.GetSqlConnection();
			try
			{
				reader = DataAccess.GetDataReader(sql);
				while (reader.Read())
				{
					ord = reader.GetOrdinal("FieldName");
					string fieldName = reader.GetString(ord);

					if (!this._fieldHashtable.Contains(fieldName))
						continue;

					FieldContext vc = (FieldContext)this._fieldHashtable[fieldName];
					sVal = "";
					ord = reader.GetOrdinal("VerifyName");
					if (!reader.IsDBNull(ord))
						sVal = reader.GetString(ord);
					vc.VerifyName = sVal;

					ord = reader.GetOrdinal("IsRequired");
					vc.IsRequired = reader.GetBoolean(ord);

					sVal = "";
					ord = reader.GetOrdinal("Range");
					if (!reader.IsDBNull(ord))
						sVal = reader.GetString(ord);
					vc.Range = sVal;

					sVal = "";
					ord = reader.GetOrdinal("ErrControlName");
					if (reader.IsDBNull(ord))
						vc.ErrorControl = null;
					else
					{
						sVal = reader.GetString(ord);
						Control ctl = ctsu.Admin.Forms.Utils.GetControlByName(sVal, this._businessLogic.Form.Controls);
						vc.ErrorControl = ctl;
					}

					ord = reader.GetOrdinal("UseNullForEmpty");
					if (!reader.IsDBNull(ord))
						vc.UseNullForEmpty = reader.GetBoolean(ord);

					ord = reader.GetOrdinal("DefaultValue");
					if (!reader.IsDBNull(ord))
						vc.DefaultValue = reader.GetString(ord);

					//get associated labels
					//TODO - future work for associated labels
					//vc.Labels = GetAssociatedLabels(dt, fieldName);				

					//raise event
					if (OnAfterLoadMetaDataEvents != null)
						OnAfterLoadMetaDataEvents();
				}
			}
			catch (Exception ex)
			{
				sb.Append("Could not retrieve meta-data!");
				sb.Append(Environment.NewLine);
				sb.Append("Error message: " + ex.Message);
				//bool b = ////ExceptionPolicy.HandleException(ex, "Exception Policy");
				return false;
			}
			finally
			{
				if (reader != null)
				{
					if (!reader.IsClosed)
						reader.Close();
				}
				//if(conn != null)
				//    conn.Dispose();
			}

			return true;
		}

		private FieldLabel GetAssociatedLabels(DataTable dt, string fieldName)
		{
			DataRow[] drs = dt.Select("FieldName='" + fieldName + "'");
			if (drs.Length == 0)
				return null;

			DataRow dr = drs[0];
			FieldLabel vcl = new FieldLabel();
			//this is the label that usually goes beside the field
			vcl.Label = (string)dr["Text"];

			//check for associated labels
			int assocID = (int)dr["AssocID"];
			if (assocID == 0)
				return vcl;

			ArrayList associated = null;
			int nextAssocID = 1;
			int type;
			string text;
			while (nextAssocID != 0)
			{
				DataRow dr1 = dt.Rows.Find(assocID);
				if (dr1 == null)
					return vcl;

				type = (int)dr1["Type"];
				nextAssocID = (int)dr1["AssocID"];
				text = (string)dr1["Text"];

				switch (type)
				{
					case 0: //section
						vcl.Section = text;
						break;
					case 1: //assoc label
						if (associated == null)
						{
							associated = new ArrayList();
							vcl.Associated = associated;
						}
						associated.Add(text);
						break;
				}
			}
			return vcl;

		}

		public bool HookEvents()
		{
			foreach (FieldContext vc in _alItems)
			{
				Control ctl = vc.Control;
				if (ctl == null)
					continue;

				string controlType = ctl.GetType().ToString();

				//for all controls
				vc.Control.Validating += new System.ComponentModel.CancelEventHandler(this._businessLogic.Central_Validating);

				switch (controlType)
				{
					case ("System.Windows.Forms.MaskedTextBox"):
						vc.Control.TextChanged += new System.EventHandler(this._businessLogic.Central_TextChanged);
						break;
					case ("System.Windows.Forms.TextBox"):
						vc.Control.TextChanged += new System.EventHandler(this._businessLogic.Central_TextChanged);
						break;
					case ("System.Windows.Forms.CheckBox"):
						((CheckBox)vc.Control).CheckedChanged += new System.EventHandler(this._businessLogic.Central_CheckedChanged);
						break;
					case ("System.Windows.Forms.ComboBox"):
						((ComboBox)vc.Control).SelectedIndexChanged += new System.EventHandler(this._businessLogic.Central_SelectedIndexChanged);
						break;
					case ("Syncfusion.Windows.Forms.Tools.DateTimePickerAdv"):
						((DateTimePickerAdv)vc.Control).ValueChanged += new System.EventHandler(this._businessLogic.Central_ValueChanged);
						break;
					case ("Syncfusion.Windows.Forms.Tools.NumericUpDownExt"):
						((NumericUpDownExt)vc.Control).ValueChanged += new System.EventHandler(this._businessLogic.Central_ValueChanged);
						break;
				}
			}

			if (this.OnAfterHookEvents != null)
				this.OnAfterHookEvents();
			return true;

		}
		#endregion one time methods for initializing field contexts
	}

	#region FieldContext
	public class FieldContext : IFieldContext
	{
		public FieldContext()
		{
		}

		private FieldContextManager _manager = null;
		private Control _control = null;
		private Control _errControl = null;
		private DataColumn _dc = null;
		private FieldLabel _vLabel = null;
		private string _verifyName = "";
		private bool _isRequired = false;
		private bool _isKey = false;
		private bool _isUnique = false;
		private bool _useNullForEmpty = false;
		private string _errMessage = "";
		private object _defaultValue = null;
		private bool _isIncomplete = true;

		private string _range = "";

		public object DefaultValue
		{
			get { return _defaultValue; }
			set { _defaultValue = value; }
		}

		public FieldContextManager Manager
		{
			get { return _manager; }
			set { _manager = value; }
		}

		public Control Control
		{
			get { return _control; }
			set { _control = value; }
		}

		public FieldLabel Labels
		{
			get { return _vLabel; }
			set { _vLabel = value; }
		}

		public string VerifyName
		{
			get { return _verifyName; }
			set { _verifyName = value; }
		}

		public string ErrorMessage
		{
			get { return _errMessage; }
			set { _errMessage = value; }
		}

		public string Range
		{
			get { return _range; }
			set { _range = value; }
		}

		public Control ErrorControl
		{
			get { return _errControl; }
			set { _errControl = value; }
		}

		public bool IsRequired
		{
			get { return _isRequired; }
			set { _isRequired = value; }
		}

		public bool IsIncomplete
		{
			get { return _isIncomplete; }
			set { _isIncomplete = value; }
		}

		public bool UseNullForEmpty
		{
			get { return _useNullForEmpty; }
			set { _useNullForEmpty = value; }
		}

		public DataColumn Column
		{
			get { return _dc; }
			set { _dc = value; }
		}

		public bool IsKey
		{
			get { return _isKey; }
			set { _isKey = value; }
		}

		public bool IsUnique
		{
			get { return _isUnique; }
			set { _isUnique = value; }
		}

		public object CurrentValue
		{
			get
			{
				if (_control == null)
					return null;

				string controlType = _control.GetType().ToString();
				switch (controlType)
				{
					case "Syncfusion.Windows.Forms.Tools.NumericUpDownExt":
						NumericUpDownExt nud = (NumericUpDownExt)_control;
						return nud.Value;

					case "System.Windows.Forms.MaskedTextBox":
						if (this.UseNullForEmpty)
						{
							if (_control.Text.Trim().Length == 0) //OR || _control.Text.Trim() == ":"
								return System.DBNull.Value;
						}
						if (_control.Text.Trim() == ":")
							return _control.Text;
						string s = _control.Text.Trim();

						int hrs = 0;
						int min = 0;
						int pos = s.IndexOf(':');
						if (pos == 0)
							hrs = 0;
						else
							hrs = int.Parse(s.Substring(0, pos));
						if (pos + 1 == s.Length)
							return System.DBNull.Value;
						min = int.Parse(s.Substring(pos + 1));
						_control.Text = hrs.ToString("00") + ":" + min.ToString("00");
						return hrs.ToString("00") + ":" + min.ToString("00");

					case "System.Windows.Forms.TextBox":
						//return null if dataType does not convert correctly
						//ie System.DateTime
						if (Column.DataType == typeof(System.Int32))
						{
							//this assumes that only numbers were allowed
							if (_control.Text.Trim().Length == 0)
								return System.DBNull.Value;
							try
							{
								return Convert.ToInt32(_control.Text);
							}
							catch
							{
								return System.DBNull.Value;
							}
						}
						else if (Column.DataType == typeof(System.DateTime))
						{
							if (_control.Text.Trim().Length == 0)
								return System.DBNull.Value;
							try
							{
								DateTime dt = Convert.ToDateTime(_control.Text);
								int mins = dt.Minute;
								dt.AddMinutes(-((double)mins));
								return dt;
							}
							catch
							{
								return System.DBNull.Value;
							}
						}
						else if (Column.DataType == typeof(System.Decimal))
						{
							if (_control.Text.Trim().Length == 0)
								return System.DBNull.Value;
							try
							{

								return Convert.ToDecimal(_control.Text);
							}
							catch
							{
								return System.DBNull.Value;
							}
						}
						else
						{
							if (this.UseNullForEmpty)
							{
								if (_control.Text.Trim().Length == 0)
									return System.DBNull.Value;
							}
							return _control.Text;
						}

					case "Syncfusion.Windows.Forms.Tools.DateTimePickerAdv":
						DateTimePickerAdv dtp = (DateTimePickerAdv)_control;
						if (dtp.IsNullDate)
							return DBNull.Value;
						else
						{
							return dtp.Value;
						}

					case "System.Windows.Forms.ComboBox":
						ComboBox cbo = (ComboBox)_control;
						if (cbo.SelectedIndex == -1)
						{
							if (this.UseNullForEmpty)
								return System.DBNull.Value;
							else if (this.DefaultValue != null)
							{
								cbo.SelectedValue = this.DefaultValue;
								return this.DefaultValue;
							}
							else
								return -1;
						}
						else
							return cbo.SelectedValue;

					case "System.Windows.Forms.CheckBox":
						CheckBox chk = (CheckBox)_control;
						return chk.Checked;

				}
				return null;
			}
		}

		/// <summary>
		/// Compares the current value with the orignal value
		/// </summary>
		/// <param name="columnName"></param>
		/// <returns></returns>
		public bool IsColumnChanged(string columnName)
		{

			object dataOrig = null;
			DataRow cr = _manager.CurrentRow;
			bool isNewRow = false;
			int iCur = 0;
			//string sCur = "";
			bool bCur = false;

			if (cr == null) { return false; }
			if (!cr.HasVersion(DataRowVersion.Original))
			{
				if (cr.RowState == DataRowState.Added)
					isNewRow = true;
				else
					return false;
			}
			else
				dataOrig = cr[columnName, DataRowVersion.Original];

			string controlType = _control.GetType().ToString();

			//handles all contol types if current value is null
			if (CurrentValue == System.DBNull.Value)
			{
				if (isNewRow)
					return false;
				else
				{
					if (dataOrig == System.DBNull.Value)
						return false;
					else
						return true;
				}
			}
			else //current value now has a value other than null
			{
				if (isNewRow)
					return true;
				if (dataOrig == System.DBNull.Value)
					return true;
			}

			//current value now has a value other than null
			switch (controlType)
			{
				case "System.Windows.Forms.TextBox":
					if (Column.DataType == typeof(System.Int32))
					{
						if ((int)CurrentValue != (int)dataOrig)
							return true;
					}
					else if (Column.DataType == typeof(System.Decimal))
					{
						if ((decimal)CurrentValue != (decimal)dataOrig)
							return true;
					}
					else if (Column.DataType == typeof(System.String)) //text
					{
						if ((string)CurrentValue != (string)dataOrig)
							return true;
					}
					break;

				case "System.Windows.Forms.MaskedTextBox":
					if ((string)CurrentValue != (string)dataOrig)
						return true;
					break;

				case "Syncfusion.Windows.Forms.Tools.DateTimePickerAdv":
					DateTime dtCur = (DateTime)CurrentValue;
					if (dtCur.CompareTo((DateTime)dataOrig) != 0)
						return true;
					break;

				case "System.Windows.Forms.ComboBox":
					iCur = (int)CurrentValue;
					if (isNewRow)
					{
						if (iCur != -9)
							return true;
					}
					else
					{
						if (dataOrig == System.DBNull.Value)
						{
							if (iCur != -9)
								return true;
						}
						else
						{
							if ((int)dataOrig != iCur)
								return true;
						}
					}
					break;
				case "System.Windows.Forms.CheckBox":
					bCur = (bool)CurrentValue;
					if (isNewRow)
					{
						if (bCur)
							return true;
					}
					else
					{
						if (dataOrig == System.DBNull.Value)
						{
							if (bCur)
								return true;
						}
						else
						{
							if ((bool)dataOrig != bCur)
								return true;
						}
					}
					break;
			}
			return false;
		}
	}

	public class FieldLabel
	{
		private string _section;
		private ArrayList _associated;
		private string _label;

		public string Section
		{
			get { return _section; }
			set { _section = value; }
		}

		public ArrayList Associated
		{
			get { return _associated; }
			set { _associated = value; }
		}

		public string Label
		{
			get { return _label; }
			set { _label = value; }
		}

		public string GetComplete()
		{
			StringBuilder sb = new StringBuilder();
			if (this._section.Length > 0)
				sb.Append(this._section);

			if (this._associated != null)
			{
				if (sb.Length > 0)
					sb.Append(Environment.NewLine);
				foreach (string s in this._associated)
				{
					sb.Append(s);
					sb.Append(Environment.NewLine);
				}
			}
			return sb.ToString();
		}

	}
	#endregion FieldContext
}
